import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:built_collection/built_collection.dart';
import 'package:mobile_app.core/collection_navigator/application/bloc/bloc.dart';
import 'package:mobile_app.core/collection_navigator/application/models/single_collection_page_model.dart';
import 'package:mobile_app.core/collection_navigator/application/models/single_custom_collection_folder_page_model.dart';
import 'package:mobile_app.core/core/application/models/app_navigator_transition_info.dart';
import 'package:mobile_app.core/core/domain/entities/learning_content/collection_list_item/collection_list_item.dart';
import 'package:mobile_app.core/core/domain/entities/learning_content/lexical_item/lexical_item.dart';
import 'package:mobile_app.core/core/domain/utils/either_extension.dart';
import 'package:mobile_app.core/core/domain/utils/with_previous_stream.dart';
import 'package:mobile_app.core/learning_content/application/bloc/bloc.dart';
import 'package:mobile_app.core/overlay/application/bloc/bloc.dart';
import 'package:mobile_app.core/purchase_flow_display/application/bloc.dart';
import 'package:mobile_app.core/user_info/application/bloc/bloc.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/application/models/collection_deletion_dialog_status_info.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/application/models/collection_folder_deletion_dialog_status_info.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/entities/collection_deletion_mode.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/entities/collection_folder_deletion_mode.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/cancel_collection_deletion/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/cancel_collection_reordering/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/complete_collection_creation/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/complete_collection_folder_deletion/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/complete_collection_folder_renaming/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/complete_collection_reordering/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/complete_selected_collection_deletion/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/deselect_all_collections_during_collection_deletion/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/get_initial_data/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/handle_collection_creation_learning_content_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/handle_collection_creation_premium_access_status_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/handle_collection_deletion_learning_content_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/handle_collection_deletion_premium_access_status_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/handle_collection_folder_renaming_learning_content_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/handle_collection_folder_renaming_premium_access_status_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/handle_collection_reordering_learning_content_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/handle_collection_reordering_premium_access_status_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/handle_default_state_learning_content_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/handle_default_state_premium_access_status_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/select_all_collections_during_collection_deletion/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/start_collection_deletion/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/start_collection_folder_deletion/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/start_selected_collection_deletion/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/swap_collections/use_case.dart';
import 'package:mobile_app.home.collections/single_custom_collection_folder_page/domain/use_cases/toggle_collection_deletion_selection/use_case.dart';
import 'package:mobile_app.home.collections.core/core/domain/helpers/validate_custom_collection_folder_title/helper.dart';
import 'package:mobile_app.home.collections.core/core/domain/helpers/validate_custom_collection_title/helper.dart';

part 'bloc_state.dart';

class SingleCustomCollectionFolderPageBloc
    extends Cubit<SingleCustomCollectionFolderPageBlocState> {
  SingleCustomCollectionFolderPageBloc({
    required SingleCustomCollectionFolderPageModel pageModel,
    required LearningContentBloc learningContentBloc,
    required UserInfoBloc userInfoBloc,
    required OverlayBloc overlayBloc,
    required CollectionNavigatorBloc collectionNavigatorBloc,
    required PurchaseFlowDisplayBloc purchaseFlowDisplayBloc,
    required GetInitialData getInitialData,
    required CancelCollectionDeletion cancelCollectionDeletion,
    required CancelCollectionReordering cancelCollectionReordering,
    required CompleteCollectionCreation completeCollectionCreation,
    required CompleteSelectedCollectionDeletion completeSelectedCollectionDeletion,
    required CompleteCollectionFolderDeletion completeCollectionFolderDeletion,
    required CompleteCollectionFolderRenaming completeCollectionFolderRenaming,
    required CompleteCollectionReordering completeCollectionReordering,
    required DeselectAllCollectionsDuringCollectionDeletion
        deselectAllCollectionsDuringCollectionDeletion,
    required HandleCollectionCreationLearningContentOuterUpdate
        handleCollectionCreationLearningContentOuterUpdate,
    required HandleCollectionCreationPremiumAccessStatusOuterUpdate
        handleCollectionCreationPremiumAccessStatusOuterUpdate,
    required HandleCollectionDeletionLearningContentOuterUpdate
        handleCollectionDeletionLearningContentOuterUpdate,
    required HandleCollectionDeletionPremiumAccessStatusOuterUpdate
        handleCollectionDeletionPremiumAccessStatusOuterUpdate,
    required HandleCollectionFolderRenamingLearningContentOuterUpdate
        handleCollectionFolderRenamingLearningContentOuterUpdate,
    required HandleCollectionFolderRenamingPremiumAccessStatusOuterUpdate
        handleCollectionFolderRenamingPremiumAccessStatusOuterUpdate,
    required HandleCollectionReorderingLearningContentOuterUpdate
        handleCollectionReorderingLearningContentOuterUpdate,
    required HandleCollectionReorderingPremiumAccessStatusOuterUpdate
        handleCollectionReorderingPremiumAccessStatusOuterUpdate,
    required HandleDefaultStateLearningContentOuterUpdate
        handleDefaultStateLearningContentOuterUpdate,
    required HandleDefaultStatePremiumAccessStatusOuterUpdate
        handleDefaultStatePremiumAccessStatusOuterUpdate,
    required SelectAllCollectionsDuringCollectionDeletion
        selectAllCollectionsDuringCollectionDeletion,
    required StartCollectionDeletion startCollectionDeletion,
    required StartCollectionFolderDeletion startCollectionFolderDeletion,
    required StartSelectedCollectionDeletion startSelectedCollectionDeletion,
    required SwapCollections swapCollections,
    required ToggleCollectionDeletionSelection toggleCollectionDeletionSelection,
  })  : _pageModel = pageModel,
        _learningContentBloc = learningContentBloc,
        _userInfoBloc = userInfoBloc,
        _overlayBloc = overlayBloc,
        _collectionNavigatorBloc = collectionNavigatorBloc,
        _purchaseFlowDisplayBloc = purchaseFlowDisplayBloc,
        _cancelCollectionDeletion = cancelCollectionDeletion,
        _cancelCollectionReordering = cancelCollectionReordering,
        _completeCollectionCreation = completeCollectionCreation,
        _completeSelectedCollectionDeletion = completeSelectedCollectionDeletion,
        _completeCollectionFolderDeletion = completeCollectionFolderDeletion,
        _completeCollectionFolderRenaming = completeCollectionFolderRenaming,
        _completeCollectionReordering = completeCollectionReordering,
        _deselectAllCollectionsDuringCollectionDeletion =
            deselectAllCollectionsDuringCollectionDeletion,
        _handleCollectionCreationLearningContentOuterUpdate =
            handleCollectionCreationLearningContentOuterUpdate,
        _handleCollectionCreationPremiumAccessStatusOuterUpdate =
            handleCollectionCreationPremiumAccessStatusOuterUpdate,
        _handleCollectionDeletionLearningContentOuterUpdate =
            handleCollectionDeletionLearningContentOuterUpdate,
        _handleCollectionDeletionPremiumAccessStatusOuterUpdate =
            handleCollectionDeletionPremiumAccessStatusOuterUpdate,
        _handleCollectionFolderRenamingLearningContentOuterUpdate =
            handleCollectionFolderRenamingLearningContentOuterUpdate,
        _handleCollectionFolderRenamingPremiumAccessStatusOuterUpdate =
            handleCollectionFolderRenamingPremiumAccessStatusOuterUpdate,
        _handleCollectionReorderingLearningContentOuterUpdate =
            handleCollectionReorderingLearningContentOuterUpdate,
        _handleCollectionReorderingPremiumAccessStatusOuterUpdate =
            handleCollectionReorderingPremiumAccessStatusOuterUpdate,
        _handleDefaultStateLearningContentOuterUpdate =
            handleDefaultStateLearningContentOuterUpdate,
        _handleDefaultStatePremiumAccessStatusOuterUpdate =
            handleDefaultStatePremiumAccessStatusOuterUpdate,
        _selectAllCollectionsDuringCollectionDeletion =
            selectAllCollectionsDuringCollectionDeletion,
        _startCollectionDeletion = startCollectionDeletion,
        _startCollectionFolderDeletion = startCollectionFolderDeletion,
        _startSelectedCollectionDeletion = startSelectedCollectionDeletion,
        _swapCollections = swapCollections,
        _toggleCollectionDeletionSelection = toggleCollectionDeletionSelection,
        super(_getInitialState(
          pageModel: pageModel,
          learningContentBlocState: learningContentBloc.state as LearningContentBlocState,
          userInfoBlocState: userInfoBloc.state as UserInfoBlocState,
          getInitialData: getInitialData,
        )) {
    streamWithPrevious = stream.withPreviousSeeded(state);

    _learningContentBlocSubscription =
        learningContentBloc.stream.listen(_onLearningContentBlocStateChanged);

    _userInfoBlocSubscription = userInfoBloc.streamWithPrevious.listen(_onUserInfoBlocStateChanged);
  }

  final SingleCustomCollectionFolderPageModel _pageModel;
  final LearningContentBloc _learningContentBloc;
  final UserInfoBloc _userInfoBloc;
  final OverlayBloc _overlayBloc;
  final CollectionNavigatorBloc _collectionNavigatorBloc;
  final PurchaseFlowDisplayBloc _purchaseFlowDisplayBloc;

  final CancelCollectionDeletion _cancelCollectionDeletion;
  final CancelCollectionReordering _cancelCollectionReordering;
  final CompleteCollectionCreation _completeCollectionCreation;
  final CompleteSelectedCollectionDeletion _completeSelectedCollectionDeletion;
  final CompleteCollectionFolderDeletion _completeCollectionFolderDeletion;
  final CompleteCollectionFolderRenaming _completeCollectionFolderRenaming;
  final CompleteCollectionReordering _completeCollectionReordering;
  final DeselectAllCollectionsDuringCollectionDeletion
      _deselectAllCollectionsDuringCollectionDeletion;
  final HandleCollectionCreationLearningContentOuterUpdate
      _handleCollectionCreationLearningContentOuterUpdate;
  final HandleCollectionCreationPremiumAccessStatusOuterUpdate
      _handleCollectionCreationPremiumAccessStatusOuterUpdate;
  final HandleCollectionDeletionLearningContentOuterUpdate
      _handleCollectionDeletionLearningContentOuterUpdate;
  final HandleCollectionDeletionPremiumAccessStatusOuterUpdate
      _handleCollectionDeletionPremiumAccessStatusOuterUpdate;
  final HandleCollectionFolderRenamingLearningContentOuterUpdate
      _handleCollectionFolderRenamingLearningContentOuterUpdate;
  final HandleCollectionFolderRenamingPremiumAccessStatusOuterUpdate
      _handleCollectionFolderRenamingPremiumAccessStatusOuterUpdate;
  final HandleCollectionReorderingLearningContentOuterUpdate
      _handleCollectionReorderingLearningContentOuterUpdate;
  final HandleCollectionReorderingPremiumAccessStatusOuterUpdate
      _handleCollectionReorderingPremiumAccessStatusOuterUpdate;
  final HandleDefaultStateLearningContentOuterUpdate _handleDefaultStateLearningContentOuterUpdate;
  final HandleDefaultStatePremiumAccessStatusOuterUpdate
      _handleDefaultStatePremiumAccessStatusOuterUpdate;
  final SelectAllCollectionsDuringCollectionDeletion _selectAllCollectionsDuringCollectionDeletion;
  final StartCollectionDeletion _startCollectionDeletion;
  final StartCollectionFolderDeletion _startCollectionFolderDeletion;
  final StartSelectedCollectionDeletion _startSelectedCollectionDeletion;
  final SwapCollections _swapCollections;
  final ToggleCollectionDeletionSelection _toggleCollectionDeletionSelection;

  late final Stream<
          (SingleCustomCollectionFolderPageBlocState, SingleCustomCollectionFolderPageBlocState)>
      streamWithPrevious;

  late final StreamSubscription<LearningContentBlocState> _learningContentBlocSubscription;
  late final StreamSubscription<(UserInfoBlocState, UserInfoBlocState?)> _userInfoBlocSubscription;

  static SingleCustomCollectionFolderPageBlocState _getInitialState({
    required SingleCustomCollectionFolderPageModel pageModel,
    required LearningContentBlocState learningContentBlocState,
    required UserInfoBlocState userInfoBlocState,
    required GetInitialData getInitialData,
  }) {
    final result = getInitialData(
      collectionFolderId: pageModel.customCollectionFolderId,
      collectionListItems: learningContentBlocState.collectionListItems,
      premiumAccessStatus: userInfoBlocState.premiumAccessStatus,
    );

    return DefaultState(
      collectionFolder: result.collectionFolder,
      permittedCollectionListItemIds: result.permittedCollectionListItemIds,
      collectionFolderExplicitlyDeleted: false,
      collectionCreationPermitted: result.collectionCreationPermitted,
      lexicalItemMap: learningContentBlocState.lexicalItemMap,
      collectionListItems: learningContentBlocState.collectionListItems,
    );
  }

  void _forceClosePage() {
    const transitionInfo = RemovalNavigatorTransitionInfo(
      hasAnimation: false,
    );

    _collectionNavigatorBloc.closePage(
      pageModel: _pageModel,
      transitionInfo: transitionInfo,
    );
  }

  void _onLearningContentBlocStateChanged(LearningContentBlocState learningContentBlocState) {
    switch (state) {
      case DefaultState():
        _onDefaultStateLearningContentBlocStateChanged(
          learningContentBlocState: learningContentBlocState,
        );

      case CollectionReordering():
        _onCollectionReorderingLearningContentBlocStateChanged(
          learningContentBlocState: learningContentBlocState,
        );

      case CollectionCreation():
        _onCollectionCreationLearningContentBlocStateChanged(
          learningContentBlocState: learningContentBlocState,
        );

      case CollectionDeletion():
        _onCollectionDeletionLearningContentBlocStateChanged(
          learningContentBlocState: learningContentBlocState,
        );

      case CollectionFolderRenaming():
        _onCollectionFolderRenamingLearningContentBlocStateChanged(
          learningContentBlocState: learningContentBlocState,
        );
    }
  }

  void _onUserInfoBlocStateChanged((UserInfoBlocState, UserInfoBlocState?) valueWithPrevious) {
    final (userInfoBlocState, userInfoBlocPreviousState) = valueWithPrevious;

    switch (state) {
      case DefaultState():
        _onDefaultStateUserInfoBlocStateChanged(
          userInfoBlocState: userInfoBlocState,
          userInfoBlocPreviousState: userInfoBlocPreviousState!,
        );

      case CollectionReordering():
        _onCollectionReorderingUserInfoBlocStateChanged(
          userInfoBlocState: userInfoBlocState,
          userInfoBlocPreviousState: userInfoBlocPreviousState!,
        );

      case CollectionCreation():
        _onCollectionCreationUserInfoBlocStateChanged(
          userInfoBlocState: userInfoBlocState,
          userInfoBlocPreviousState: userInfoBlocPreviousState!,
        );

      case CollectionDeletion():
        _onCollectionDeletionUserInfoBlocStateChanged(
          userInfoBlocState: userInfoBlocState,
          userInfoBlocPreviousState: userInfoBlocPreviousState!,
        );

      case CollectionFolderRenaming():
        _onCollectionFolderRenamingUserInfoBlocStateChanged(
          userInfoBlocState: userInfoBlocState,
          userInfoBlocPreviousState: userInfoBlocPreviousState!,
        );
    }
  }

  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// [DefaultState]
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // <editor-fold defaultstate="collapsed">

  void _onDefaultStateLearningContentBlocStateChanged({
    required LearningContentBlocState learningContentBlocState,
  }) {
    final _state = state as DefaultState;

    if (_state.lexicalItemMap == learningContentBlocState.lexicalItemMap &&
        _state.collectionListItems == learningContentBlocState.collectionListItems) {
      return;
    }

    final failureOrResult = _handleDefaultStateLearningContentOuterUpdate(
      collectionFolderId: _pageModel.customCollectionFolderId,
      collectionListItems: learningContentBlocState.collectionListItems,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
    );

    if (failureOrResult.isLeft()) {
      final failure = failureOrResult.asLeft();

      if (failure
          is NonexistentCollectionFolderDefaultStateLearningContentOuterUpdateHandlingFailure) {
        _forceClosePage();
        return;
      }

      throw UnimplementedError();
    }

    final result = failureOrResult.asRight();

    emit(_state.copyWith(
      collectionFolder: () => result.collectionFolder,
      permittedCollectionListItemIds: () => result.permittedCollectionListItemIds,
    ));
  }

  void _onDefaultStateUserInfoBlocStateChanged({
    required UserInfoBlocState userInfoBlocState,
    required UserInfoBlocState userInfoBlocPreviousState,
  }) {
    final _state = state as DefaultState;

    if (userInfoBlocState.premiumAccessStatus == userInfoBlocPreviousState.premiumAccessStatus) {
      return;
    }

    final result = _handleDefaultStatePremiumAccessStatusOuterUpdate(
      collectionFolderId: _pageModel.customCollectionFolderId,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      premiumAccessStatus: userInfoBlocState.premiumAccessStatus,
    );

    emit(_state.copyWith(
      permittedCollectionListItemIds: () => result.permittedCollectionListItemIds,
      collectionCreationPermitted: () => result.collectionCreationPermitted,
    ));
  }

  void requestCollectionFolderRenamingStart() {
    final _state = state;

    if (_state is! DefaultState || _state.collectionFolderExplicitlyDeleted) {
      return;
    }

    emit(CollectionFolderRenaming(
      collectionFolder: _state.collectionFolder,
      permittedCollectionListItemIds: _state.permittedCollectionListItemIds,
      collectionCreationPermitted: _state.collectionCreationPermitted,
    ));
  }

  void requestCollectionCreationStart() {
    final _state = state;

    if (_state is! DefaultState || _state.collectionFolderExplicitlyDeleted) {
      return;
    }

    if (!_state.collectionCreationPermitted) {
      _purchaseFlowDisplayBloc.openPurchasePage();
      return;
    }

    emit(CollectionCreation(
      collectionFolder: _state.collectionFolder,
      permittedCollectionListItemIds: _state.permittedCollectionListItemIds,
      collectionCreationPermitted: _state.collectionCreationPermitted,
    ));
  }

  void requestCollectionReorderingStart() {
    final _state = state;

    if (_state is! DefaultState || _state.collectionFolderExplicitlyDeleted) {
      return;
    }

    emit(CollectionReordering(
      permittedCollectionListItemIds: _state.permittedCollectionListItemIds,
      reorderedCollections: _state.collectionFolder.collections,
      collectionFolder: _state.collectionFolder,
    ));
  }

  void requestCollectionDeletionStart() {
    final _state = state;

    if (_state is! DefaultState || _state.collectionFolderExplicitlyDeleted) {
      return;
    }

    final result = _startCollectionDeletion();

    emit(CollectionDeletion(
      collectionFolder: _state.collectionFolder,
      permittedCollectionListItemIds: _state.permittedCollectionListItemIds,
      selectedCollectionIds: result.selectedCollectionIds,
    ));
  }

  void requestCollectionFolderDeletionStart() {
    final _state = state;

    if (_state is! DefaultState || _state.collectionFolderExplicitlyDeleted) {
      return;
    }

    final result = _startCollectionFolderDeletion(
      collectionFolder: _state.collectionFolder,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
    );

    final collectionFolderDeletionDialogStatusInfo = CollectionFolderDeletionDialogStatusInfo(
      shown: true,
      deletionModes: result.deletionModes,
    );

    emit(_state.copyWith(
      collectionFolderDeletionDialogStatusInfo: () => collectionFolderDeletionDialogStatusInfo,
    ));
  }

  void requestCollectionFolderDeletionCompletion({
    required CollectionFolderDeletionMode deletionMode,
  }) {
    final _state = state;

    if (_state is! DefaultState || _state.collectionFolderExplicitlyDeleted) {
      return;
    }

    final failureOrResult = _completeCollectionFolderDeletion(
      collectionFolder: _state.collectionFolder,
      deletionMode: deletionMode,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
    );

    if (failureOrResult.isLeft()) {
      final failure = failureOrResult.asLeft();

      switch (failure) {
        case CustomLexicalItemNotExclusiveCollectionFolderDeletionCompletionFailure():
          emit(_state.copyWith(
            customLexicalItemDeletionDialogShown: () => true,
          ));
      }

      return;
    }

    final result = failureOrResult.asRight();

    emit(_state.copyWith(
      lexicalItemMap: () => result.lexicalItemMap,
      collectionListItems: () => result.collectionListItems,
      collectionFolderExplicitlyDeleted: () => true,
    ));

    _collectionNavigatorBloc.handlePop();

    _overlayBloc.showSuccessIndicator();

    _learningContentBloc.updateWith(
      lexicalItemMap: () => result.lexicalItemMap,
      collectionListItems: () => result.collectionListItems,
    );
  }

  void requestSingleCollectionPageOpening({
    required String collectionId,
  }) {
    final _state = state;

    if (_state is! DefaultState || _state.collectionFolderExplicitlyDeleted) {
      return;
    }

    final collectionLexicalItemPageModel = SingleCollectionPageModel(
      collectionId: collectionId,
    );

    _collectionNavigatorBloc.pushPage(
      pageModel: collectionLexicalItemPageModel,
    );
  }

  // </editor-fold>

  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// [CollectionReordering]
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // <editor-fold defaultstate="collapsed">

  void _onCollectionReorderingLearningContentBlocStateChanged({
    required LearningContentBlocState learningContentBlocState,
  }) {
    final _state = state as CollectionReordering;

    final failureOrResult = _handleCollectionReorderingLearningContentOuterUpdate(
      collectionFolderId: _pageModel.customCollectionFolderId,
      reorderedCollections: _state.reorderedCollections,
      collectionListItems: learningContentBlocState.collectionListItems,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
    );

    if (failureOrResult.isLeft()) {
      final failure = failureOrResult.asLeft();

      if (failure
          is NonexistentCollectionFolderCollectionReorderingLearningContentOuterUpdateHandlingFailure) {
        _forceClosePage();
        return;
      }

      if (failure
          is TooFewCollectionsCollectionReorderingLearningContentOuterUpdateHandlingFailure) {
        emit(DefaultState(
          collectionFolder: failure.collectionFolder,
          permittedCollectionListItemIds: failure.permittedCollectionListItemIds,
          collectionCreationPermitted: failure.collectionCreationPermitted,
          collectionFolderExplicitlyDeleted: false,
          lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
          collectionListItems: _learningContentBloc.state!.collectionListItems,
        ));

        return;
      }

      throw UnimplementedError();
    }

    final result = failureOrResult.asRight();

    emit(_state.copyWith(
      permittedCollectionListItemIds: () => result.permittedCollectionListItemIds,
      reorderedCollections: () => result.reorderedCollections,
    ));
  }

  void _onCollectionReorderingUserInfoBlocStateChanged({
    required UserInfoBlocState userInfoBlocState,
    required UserInfoBlocState userInfoBlocPreviousState,
  }) {
    final _state = state as CollectionReordering;

    if (userInfoBlocState.premiumAccessStatus == userInfoBlocPreviousState.premiumAccessStatus) {
      return;
    }

    final result = _handleCollectionReorderingPremiumAccessStatusOuterUpdate(
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      premiumAccessStatus: userInfoBlocState.premiumAccessStatus,
    );

    emit(_state.copyWith(
      permittedCollectionListItemIds: () => result.permittedCollectionListItemIds,
    ));
  }

  void requestCollectionReorderingCancellation() {
    final _state = state;

    if (_state is! CollectionReordering) {
      return;
    }

    final result = _cancelCollectionReordering(
      collectionFolderId: _pageModel.customCollectionFolderId,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      permittedCollectionListItemIds: _state.permittedCollectionListItemIds,
    );

    emit(DefaultState(
      collectionFolder: result.collectionFolder,
      permittedCollectionListItemIds: _state.permittedCollectionListItemIds,
      collectionCreationPermitted: result.collectionCreationPermitted,
      collectionFolderExplicitlyDeleted: false,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
    ));
  }

  void requestCollectionReorderingCompletion() {
    final _state = state;

    if (_state is! CollectionReordering) {
      return;
    }

    final result = _completeCollectionReordering(
      collectionFolderId: _pageModel.customCollectionFolderId,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      reorderedCollections: _state.reorderedCollections,
      permittedCollectionListItemIds: _state.permittedCollectionListItemIds,
    );

    emit(DefaultState(
      collectionFolder: result.collectionFolder,
      permittedCollectionListItemIds: _state.permittedCollectionListItemIds,
      collectionCreationPermitted: result.collectionCreationPermitted,
      collectionFolderExplicitlyDeleted: false,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      collectionListItems: result.collectionListItems,
    ));

    _overlayBloc.showSuccessIndicator();

    _learningContentBloc.updateWith(
      collectionListItems: () => result.collectionListItems,
    );
  }

  void requestReorderableCollectionSwap({
    required int firstIndex,
    required int secondIndex,
  }) {
    final _state = state;

    if (_state is! CollectionReordering) {
      return;
    }

    final result = _swapCollections(
      collections: _state.reorderedCollections,
      firstIndex: firstIndex,
      secondIndex: secondIndex,
    );

    emit(_state.copyWith(
      reorderedCollections: () => result.collections,
    ));
  }

  // </editor-fold>

  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// [CollectionCreation]
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // <editor-fold defaultstate="collapsed">

  void _onCollectionCreationLearningContentBlocStateChanged({
    required LearningContentBlocState learningContentBlocState,
  }) {
    final _state = state as CollectionCreation;

    final failureOrResult = _handleCollectionCreationLearningContentOuterUpdate(
      collectionFolderId: _pageModel.customCollectionFolderId,
      collectionListItems: learningContentBlocState.collectionListItems,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
    );

    if (failureOrResult.isLeft()) {
      final failure = failureOrResult.asLeft();

      if (failure
          is NonexistentCollectionFolderCollectionCreationLearningContentOuterUpdateHandlingFailure) {
        _forceClosePage();
        return;
      }

      throw UnimplementedError();
    }

    final result = failureOrResult.asRight();

    emit(_state.copyWith(
      collectionFolder: () => result.collectionFolder,
      permittedCollectionListItemIds: () => result.permittedCollectionListItemIds,
    ));
  }

  void _onCollectionCreationUserInfoBlocStateChanged({
    required UserInfoBlocState userInfoBlocState,
    required UserInfoBlocState userInfoBlocPreviousState,
  }) {
    final _state = state as CollectionCreation;

    if (userInfoBlocState.premiumAccessStatus == userInfoBlocPreviousState.premiumAccessStatus) {
      return;
    }

    final result = _handleCollectionCreationPremiumAccessStatusOuterUpdate(
      collectionFolderId: _pageModel.customCollectionFolderId,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      premiumAccessStatus: userInfoBlocState.premiumAccessStatus,
    );

    emit(_state.copyWith(
      permittedCollectionListItemIds: () => result.permittedCollectionListItemIds,
      collectionCreationPermitted: () => result.collectionCreationPermitted,
    ));
  }

  void requestCollectionCreationCancellation() {
    final _state = state;

    if (_state is! CollectionCreation) {
      return;
    }

    emit(DefaultState(
      collectionFolder: _state.collectionFolder,
      permittedCollectionListItemIds: _state.permittedCollectionListItemIds,
      collectionCreationPermitted: _state.collectionCreationPermitted,
      collectionFolderExplicitlyDeleted: false,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
    ));
  }

  void requestCollectionCreationCompletion({
    required String collectionTitleInput,
  }) {
    final _state = state;

    if (_state is! CollectionCreation) {
      return;
    }

    final failureOrResult = _completeCollectionCreation(
      collectionFolder: _state.collectionFolder,
      collectionTitleInput: collectionTitleInput,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
    );

    if (failureOrResult.isLeft()) {
      final failure = failureOrResult.asLeft();

      switch (failure) {
        case InvalidCollectionTitleCollectionCreationCompletionFailure():
          emit(_state.copyWith(
            validationError: () => failure.validationError,
          ));
      }

      return;
    }

    final result = failureOrResult.asRight();

    emit(DefaultState(
      collectionFolder: result.collectionFolder,
      permittedCollectionListItemIds: result.permittedCollectionListItemIds,
      collectionCreationPermitted: _state.collectionCreationPermitted,
      collectionFolderExplicitlyDeleted: false,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      collectionListItems: result.collectionListItems,
    ));

    _overlayBloc.showSuccessIndicator();

    _learningContentBloc.updateWith(
      collectionListItems: () => result.collectionListItems,
    );
  }

  // </editor-fold>

  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// [CollectionDeletion]
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // <editor-fold defaultstate="collapsed">

  void _onCollectionDeletionLearningContentBlocStateChanged({
    required LearningContentBlocState learningContentBlocState,
  }) {
    final _state = state as CollectionDeletion;

    final failureOrResult = _handleCollectionDeletionLearningContentOuterUpdate(
      collectionFolderId: _pageModel.customCollectionFolderId,
      selectedCollectionIds: _state.selectedCollectionIds,
      collectionListItems: learningContentBlocState.collectionListItems,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
    );

    if (failureOrResult.isLeft()) {
      final failure = failureOrResult.asLeft();

      if (failure
          is NonexistentCollectionFolderCollectionDeletionLearningContentOuterUpdateHandlingFailure) {
        _forceClosePage();
        return;
      }

      if (failure
          is EmptyCollectionFolderCollectionDeletionLearningContentOuterUpdateHandlingFailure) {
        emit(DefaultState(
          collectionFolder: failure.collectionFolder,
          permittedCollectionListItemIds: failure.permittedCollectionListItemIds,
          collectionCreationPermitted: failure.collectionCreationPermitted,
          collectionFolderExplicitlyDeleted: false,
          lexicalItemMap: learningContentBlocState.lexicalItemMap,
          collectionListItems: learningContentBlocState.collectionListItems,
        ));

        return;
      }

      throw UnimplementedError();
    }

    final result = failureOrResult.asRight();

    emit(_state.copyWith(
      collectionFolder: () => result.collectionFolder,
      permittedCollectionListItemIds: () => result.permittedCollectionListItemIds,
    ));
  }

  void _onCollectionDeletionUserInfoBlocStateChanged({
    required UserInfoBlocState userInfoBlocState,
    required UserInfoBlocState userInfoBlocPreviousState,
  }) {
    final _state = state as CollectionDeletion;

    if (userInfoBlocState.premiumAccessStatus == userInfoBlocPreviousState.premiumAccessStatus) {
      return;
    }

    final result = _handleCollectionDeletionPremiumAccessStatusOuterUpdate(
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      premiumAccessStatus: userInfoBlocState.premiumAccessStatus,
    );

    emit(_state.copyWith(
      permittedCollectionListItemIds: () => result.permittedCollectionListItemIds,
    ));
  }

  void requestCollectionDeletionCancellation() {
    final _state = state;

    if (_state is! CollectionDeletion) {
      return;
    }

    final result = _cancelCollectionDeletion(
      collectionFolderId: _pageModel.customCollectionFolderId,
      permittedCollectionListItemIds: _state.permittedCollectionListItemIds,
    );

    emit(DefaultState(
      collectionFolder: _state.collectionFolder,
      permittedCollectionListItemIds: _state.permittedCollectionListItemIds,
      collectionCreationPermitted: result.collectionCreationPermitted,
      collectionFolderExplicitlyDeleted: false,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
    ));
  }

  void requestSelectedCollectionDeletionStart() {
    final _state = state;

    if (_state is! CollectionDeletion) {
      return;
    }

    final result = _startSelectedCollectionDeletion(
      collectionFolder: _state.collectionFolder,
      selectedCollectionIds: _state.selectedCollectionIds,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
    );

    final collectionDeletionDialogStatusInfo = CollectionDeletionDialogStatusInfo(
      shown: true,
      deletionModes: result.deletionModes,
    );

    emit(_state.copyWith(
      collectionDeletionDialogStatusInfo: () => collectionDeletionDialogStatusInfo,
    ));
  }

  void requestSelectedCollectionDeletionCompletion({
    required CollectionDeletionMode deletionMode,
  }) {
    final _state = state;

    if (_state is! CollectionDeletion) {
      return;
    }

    final failureOrResult = _completeSelectedCollectionDeletion(
      collectionFolder: _state.collectionFolder,
      deletionMode: deletionMode,
      selectedCollectionIds: _state.selectedCollectionIds,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
    );

    if (failureOrResult.isLeft()) {
      final failure = failureOrResult.asLeft();

      switch (failure) {
        case CustomLexicalItemNotExclusiveSelectedCollectionDeletionCompletionFailure():
          emit(_state.copyWith(
            customLexicalItemDeletionDialogShown: () => true,
          ));
      }

      return;
    }

    final result = failureOrResult.asRight();

    emit(DefaultState(
      collectionFolder: result.collectionFolder,
      permittedCollectionListItemIds: result.permittedCollectionListItemIds,
      collectionCreationPermitted: result.collectionCreationPermitted,
      collectionFolderExplicitlyDeleted: false,
      lexicalItemMap: result.lexicalItemMap,
      collectionListItems: result.collectionListItems,
    ));

    _overlayBloc.showSuccessIndicator();

    _learningContentBloc.updateWith(
      lexicalItemMap: () => result.lexicalItemMap,
      collectionListItems: () => result.collectionListItems,
    );
  }

  void requestCollectionDeletionSelectionToggling({
    required String collectionId,
  }) {
    final _state = state;

    if (_state is! CollectionDeletion) {
      return;
    }

    final result = _toggleCollectionDeletionSelection(
      collectionId: collectionId,
      selectedCollectionIds: _state.selectedCollectionIds,
    );

    emit(_state.copyWith(
      selectedCollectionIds: () => result.selectedCollectionIds,
    ));
  }

  void requestCollectionDeletionAllCollectionSelection() {
    final _state = state;

    if (_state is! CollectionDeletion) {
      return;
    }

    final result = _selectAllCollectionsDuringCollectionDeletion(
      collectionFolder: _state.collectionFolder,
    );

    emit(_state.copyWith(
      selectedCollectionIds: () => result.selectedCollectionIds,
    ));
  }

  void requestCollectionDeletionAllCollectionDeselection() {
    final _state = state;

    if (_state is! CollectionDeletion) {
      return;
    }

    final result = _deselectAllCollectionsDuringCollectionDeletion();

    emit(_state.copyWith(
      selectedCollectionIds: () => result.selectedCollectionIds,
    ));
  }

  // </editor-fold>

  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// [CollectionFolderRenaming]
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // <editor-fold defaultstate="collapsed">

  void _onCollectionFolderRenamingLearningContentBlocStateChanged({
    required LearningContentBlocState learningContentBlocState,
  }) {
    final _state = state as CollectionFolderRenaming;

    final failureOrResult = _handleCollectionFolderRenamingLearningContentOuterUpdate(
      collectionFolderId: _pageModel.customCollectionFolderId,
      collectionListItems: learningContentBlocState.collectionListItems,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
    );

    if (failureOrResult.isLeft()) {
      final failure = failureOrResult.asLeft();

      if (failure
          is NonexistentCollectionFolderCollectionFolderRenamingLearningContentOuterUpdateHandlingFailure) {
        _forceClosePage();
        return;
      }

      throw UnimplementedError();
    }

    final result = failureOrResult.asRight();

    emit(_state.copyWith(
      collectionFolder: () => result.collectionFolder,
      permittedCollectionListItemIds: () => result.permittedCollectionListItemIds,
    ));
  }

  void _onCollectionFolderRenamingUserInfoBlocStateChanged({
    required UserInfoBlocState userInfoBlocState,
    required UserInfoBlocState userInfoBlocPreviousState,
  }) {
    final _state = state as CollectionFolderRenaming;

    if (userInfoBlocState.premiumAccessStatus == userInfoBlocPreviousState.premiumAccessStatus) {
      return;
    }

    final result = _handleCollectionFolderRenamingPremiumAccessStatusOuterUpdate(
      collectionFolderId: _pageModel.customCollectionFolderId,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      premiumAccessStatus: userInfoBlocState.premiumAccessStatus,
    );

    emit(_state.copyWith(
      permittedCollectionListItemIds: () => result.permittedCollectionListItemIds,
      collectionCreationPermitted: () => result.collectionCreationPermitted,
    ));
  }

  void requestCollectionFolderRenamingCancellation() {
    final _state = state;

    if (_state is! CollectionFolderRenaming) {
      return;
    }

    emit(DefaultState(
      collectionFolder: _state.collectionFolder,
      permittedCollectionListItemIds: _state.permittedCollectionListItemIds,
      collectionCreationPermitted: _state.collectionCreationPermitted,
      collectionFolderExplicitlyDeleted: false,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
    ));
  }

  void requestCollectionFolderRenamingCompletion({
    required String collectionFolderTitleInput,
  }) {
    final _state = state;

    if (_state is! CollectionFolderRenaming) {
      return;
    }

    final failureOrResult = _completeCollectionFolderRenaming(
      collectionFolder: _state.collectionFolder,
      collectionFolderTitleInput: collectionFolderTitleInput,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
    );

    if (failureOrResult.isLeft()) {
      final failure = failureOrResult.asLeft();

      switch (failure) {
        case InvalidCollectionTitleCollectionFolderRenamingCompletionFailure():
          emit(_state.copyWith(
            validationError: () => failure.validationError,
          ));
      }

      return;
    }

    final result = failureOrResult.asRight();

    emit(DefaultState(
      collectionFolder: result.collectionFolder,
      permittedCollectionListItemIds: _state.permittedCollectionListItemIds,
      collectionCreationPermitted: _state.collectionCreationPermitted,
      collectionFolderExplicitlyDeleted: false,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      collectionListItems: result.collectionListItems,
    ));

    _overlayBloc.showSuccessIndicator();

    _learningContentBloc.updateWith(
      collectionListItems: () => result.collectionListItems,
    );
  }

  // </editor-fold>

  @override
  Future<void> close() {
    _learningContentBlocSubscription.cancel();
    _userInfoBlocSubscription.cancel();
    return super.close();
  }
}
